﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using TanmiaGrp.Basis;
using TanmiaGrp.Basis.Specification;
using TanmiaGrp.GeneralLedger.Core;
using TanmiaGrp.GeneralLedger.IRepository;

namespace TanmiaGrp.GeneralLedger.Domain
{
    public class JournalDomain : BaseDomain<Journal, JournalDomain>
    {
        public override List<Journal> GetPage(int pageIndex)
        {
            return QueryRepository.LoadAll<Journal, DateTime>(new GenericSpecification<Journal>(pageIndex, PageSize), o => o.CreateDate);
        }

        public override List<Journal> SearchPage(SearchSpecification searchSpecs)
        {
            return QueryRepository.LoadAll<Journal, DateTime>(CreateSpecification(searchSpecs), o => o.CreateDate);
        }

        /// <summary>
        /// Get full aggregate graph of the Journal.
        /// </summary>
        public Journal GetAggregateRoot(Journal entity)
        {
            Contract.Assert(entity != null, "entity parameter must not be null.");
            Contract.Assert(entity.JournalID > 0, "entity.JournalID must be greater than zero.");

            return QueryRepository.Single<Journal>(x => x.JournalID == entity.JournalID);
        }

        public bool Post(Journal journal)
        {
            Contract.Assert(journal != null, "اليومية يجب أن لا تكون فارغة.");

            if (journal.PostState == PostStateEnum.IsPosted)
                throw new DomainException("اليومية يجب أن تكون جديدة أو غير مرحلة حتى يتم ترحيلها.");

            Journal fullJournal = GetAggregateRoot(journal);
            Voucher postingVoucher = fullJournal.ToPostVoucher();

            using (IUnitOfWork uow = RepositoryFactory.CreateUnitOfWork())
            {
                // 1. Create POST Voucher+Details in the repository
                VoucherDomain.Obj.Add(postingVoucher, uow);

                // 2. Update ChartOfAccounts Tree
                foreach (VoucherDetail detail in postingVoucher.VoucherDetail)
                    if (!AccountDomain.Obj.UpdateBalancesTree(detail.Account, detail.Debit, detail.Credit, uow))
                        return false;

                // 3. Update Journal Status
                Journal finalizedJournal = uow.QueryRepository.Single<Journal>(x => x.JournalID == journal.JournalID);
                finalizedJournal.PostState = PostStateEnum.IsPosted;
                finalizedJournal.LastUpdateDate = DateTime.Now;
                uow.Repository.Update<Journal>(finalizedJournal);

                // 4. Commit Changes
                int erc = uow.Commit();
                return erc > 0;
            }
        }

        public bool UnPost(Journal journal)
        {
            Contract.Assert(journal != null, "journal parameter must not be null.");

            if (journal.PostState != PostStateEnum.IsPosted)
                throw new DomainException("لا يمكن فك ترحيل يومية غير مرحلة");

            Journal fullJournal = GetAggregateRoot(journal);
            Voucher unpostingVoucher = fullJournal.ToUnpostVoucher();

            using (IUnitOfWork uow = RepositoryFactory.CreateUnitOfWork())
            {
                // 1. Create Voucher+Details in the Repository
                VoucherDomain.Obj.Add(unpostingVoucher, uow);

                // 2. Update ChartOfAccounts Tree
                foreach (VoucherDetail detail in unpostingVoucher.VoucherDetail)
                    if (!AccountDomain.Obj.UpdateBalancesTree(detail.Account, detail.Debit, detail.Credit, uow))
                        return false;

                // 3. Update Journal.PostState
                Journal finalizedJournal = uow.QueryRepository.Single<Journal>(x => x.JournalID == fullJournal.JournalID);
                finalizedJournal.PostState = PostStateEnum.IsUnposted;
                finalizedJournal.LastUpdateDate = DateTime.Now;
                uow.Repository.Update<Journal>(finalizedJournal);

                // 4. commit changes
                int erc = uow.Commit();
                return erc > 0;
            }
        }

        private GenericSpecification<Journal> CreateSpecification(SearchSpecification searchSpecs)
        {
            return new GenericSpecification<Journal>(
                pageIndex: searchSpecs.PageIndex,
                pageSize: ConfigManager.Obj.PageSize);
        }
    }
}
